import sys
import os
import subprocess
import time
import datetime
import logging
import logging.handlers
import MySQLdb as sql

LOGFILE = '/var/log/nntools/update.log'
RECALL = '/tmp/backup/recall.txt'
LOG_FORMAT = '%(asctime)s - %(message)s'


PHP_PATH = '/usr/bin/php'
PY_PATH = '/usr/bin/python'
NN_DIR = '/var/www/newznab'
NN_UPDATE = 'misc/update_scripts'
NN_TESTING = 'misc/testing'
MYSQL_DBNAME = 'newznab'
MYSQL_USER = 'root'
MYSQL_PASS = 'f0r3sw0rn'
MYSQL_HOST = 'localhost'

BACKLOG = """select COUNT(*) from releases r left join category c on c.ID = r.categoryID
             where (r.passwordstatus between -6 and -1)
              or (r.haspreview = -1 and c.disablepreview = 0);"""

KEEPUP = 5
DO_BACKFILL = False

if not MYSQL_PASS:
    print 'No MySQL password entered.'
    sys.exit()

updateBinaries = [PHP_PATH, os.path.join(NN_DIR, NN_UPDATE, 'update_binaries_threaded.php')]
updateReleases = [PHP_PATH, os.path.join(NN_DIR, NN_UPDATE, 'update_releases.php')]
updateBackfill = [PHP_PATH, os.path.join(NN_DIR, NN_UPDATE, 'backfill_threaded.php')]

OPTIMIZE = [ [PHP_PATH, os.path.join(NN_DIR, NN_TESTING, 'removespecial.php')],
             [PHP_PATH, os.path.join(NN_DIR, NN_TESTING, 'update_parsing.php')],
             [PHP_PATH, os.path.join(NN_DIR, NN_TESTING, 'updateCategories.php')],
             [PHP_PATH, os.path.join(NN_DIR, NN_TESTING, 'update_cleanup.php')],
             [PHP_PATH, os.path.join(NN_DIR, NN_UPDATE, 'optimise_db.php')],
             [PHP_PATH, os.path.join(NN_DIR, NN_UPDATE, 'update_tvschedule.php')],
             [PHP_PATH, os.path.join(NN_DIR, NN_UPDATE, 'update_theaters.php')] ]

os.chdir(os.path.join(NN_DIR, NN_UPDATE))

lgr = logging.getLogger('updadte')
lgr.setLevel(logging.DEBUG)
fh = logging.handlers.TimedRotatingFileHandler(LOGFILE, when='midnight', interval=1, backupCount=3)
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.DEBUG)
fh.setLevel(logging.DEBUG)
frmt = logging.Formatter(LOG_FORMAT)
fh.setFormatter(frmt)
sh.setFormatter(frmt)
lgr.addHandler(fh)
lgr.addHandler(sh)


def sqlConnect():
    try:
        conn = sql.connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DBNAME)
    except sql.Error as e:
        lgr.error('Error accessing db {}: {}'.format(MYSQL_DBNAME, e))
        return False
    else:
        lgr.info('Connection successful.')
        conn.close()
        return True

def getBacklog():
    data = None
    conn = sqlConnect()
    if conn:
        cursor = conn.cursor()
        cursor.execute(BACKLOG)
        data = int(cursor.fetchone()[0])
        conn.close()
    return data

def setBackfillDate():
    conn = sqlConnect()
    if conn:
        cursor = conn.cursor()
        cursor.execute(BACKFILL)
        conn.close()
        
def getBinaries():
    lgr.info('Updating binaries.')
    try:
        subprocess.check_call(updateBinaries)
    except subprocess.CalledProcessError as e:
        lgr.error('Updating binaries failed: {}'.format(e))
        return False
    else:
        return True

def doReleases():
    lgr.debug('Processing releases.')
    try:
        subprocess.check_call(updateReleases)
    except subprocess.CalledProcessError as e:
        lgr.warning('Process update_releases crashed: {}'.format(e))
        return False
    else:
        lgr.debug('Finished processing releases.')
        return True

def doBackfill():
    lgr.info('Doing backfill.')
    try:
        subprocess.check_call(updateBackfill)
    except subprocess.CalledProcessError as e:
        lgr.warning('Backfill error: {}'.format(e))
        return False
    else:
        lgr.info('Finished backfill.')
        return True
        
def doOptimize(date):
    lgr.info('Checking time.')
    if not (datetime.datetime.now() - date).seconds >= 43200:
        lgr.info('Not time to optimize yet.')
        return
    lgr.info('Running optimizations')
    for cmd in OPTIMIZE:
        try:
            subprocess.check_call(cmd)
        except subprocess.CalledProcessError as e:
            lgr.warning('Optimization failed on command: {}\nError: {}'.format(cmd[-1]), e)
        else:
            lgr.debug('Optimization subtask complete: {}'.format(cmd[-1]))
    lgr.info('Optimization complete.')

def checkBackup():
    try:
        with open(RECALL, 'r') as f:
            b = f.read()
    except:
        return
    if 'stop_update' in b:
        time.sleep(3)
        with open(RECALL, 'a') as f:
            f.write('update_waiting\n')
        lgr.info('Pause request received.  Waiting to resume.')
    while True:
        doSleep(600)
        if not os.path.exists(RECALL):
            lgr.info('Recall file not found, resuming.')
            return
            
def doSleep(t):
    lgr.warning('Waiting {} seconds.'.format(t))
    time.sleep(t)
    
def updateWithNoBackfill():
    if not sqlConnect():
        lgr.error('Could not connect to DB.  Aborting script.')
        sys.exit()
   
    while True:
        startTime = datetime.datetime.now()
        if not getBinaries():
            lgr.error('Failed getting binaries.  Aborting script.')
        doReleases()
        counter = 0
        while getBacklog() > 100 and counter < KEEPUP:
            doReleases()
            counter += 1
            checkBackup()
        checkBackup()
        doOptimize(startTime)
        doSleep(600)
        
def updateWithBackfill():
    lgr.error('Backfill not implemented.')
        
def main():
    if DO_BACKFILL:
        updateWithBackfill()
    updateWithNoBackfill()
        
if __name__ == "__main__":
    main()
    
